﻿using Sidvall.WorkItemManager.Data;
using Sidvall.WorkItemManager.Workflow;
using System.Linq;
using System.Threading.Tasks;

namespace Sidvall.WorkItemManager.UI.Entities
{
    public sealed partial class TreeItemCollection : Sidvall.WorkItemManager.UI.Entities.Core.BusinessBaseCollection<TreeItem>
    {
        #region Public Members

        #region FindItem

        public TreeItem FindItem(Sidvall.WorkItemManager.Data.IWorkItem item)
        {
            if (item == null)
                return null;
            if (item.ServerConnectionId == null)
                return null;
            if (item.WorkItemId == null)
                return null;
            return FindItem(item.ServerConnectionId.Value, item.WorkItemId.Value);
        }
        public TreeItem FindItem(int serverConnectionId, long workItemId)
        {
            return (from o in this
                    where (o.ServerConnectionId == serverConnectionId) && (o.ItemId == workItemId)
                    select o).FirstOrDefault();
        }

        #endregion
        #region ReloadAsync

        public async Task ReloadAsync(Sidvall.WorkItemManager.Data.TreeType type)
        {
            System.Collections.Generic.List<System.Threading.Tasks.Task> tasks;
            System.Collections.ObjectModel.Collection<DataFetchResult> dataFetchResults;
            Sidvall.WorkItemManager.Net.Services.V1.ServerConnection serverConnection;
            System.Collections.Generic.HashSet<string> ids;
            TreeItem treeItem;
            DataFetchResult dataFetchResult;
            int iMax, jMax;

            // Prepare tasks
            dataFetchResults = new System.Collections.ObjectModel.Collection<DataFetchResult>();
            foreach (var serverManager in Sidvall.WorkItemManager.UI.SystemContext.Current.ServerManagers.ValidItems)
            {
                serverConnection = (from o in Sidvall.WorkItemManager.SystemContext.Current.UserConfiguration.ServerConnections.Items
                                    where o.ServerConnectionId == serverManager.ServerConnectionId
                                    select o).FirstOrDefault();
                dataFetchResult = new DataFetchResult()
                {
                    ServerManager = serverManager,
                    ServerConnection = serverConnection,
                };
                dataFetchResults.Add(dataFetchResult);
            }

            // Create tasks
            tasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
            iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
                tasks.Add(ReloadAsync(dataFetchResults, type, i));
            if (tasks.Count > 0)
                await System.Threading.Tasks.Task.WhenAll(tasks);

            // Reload items
            iMax = dataFetchResults.Count;
            for (int i = 0; i < iMax; i++)
            {
                dataFetchResult = dataFetchResults[i];
                if (dataFetchResult.Exception != null)
                    continue;
                ids = new System.Collections.Generic.HashSet<string>();

                // Servers
                ProcessServer(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager);

                // Items
                switch (type)
                {
                    case TreeType.WorkItemType:
                        foreach (var workItemType in dataFetchResult.WorkItemTypes)
                            ProcessItem(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, workItemType);
                        break;
                    case TreeType.Condition:
                        foreach (var workCondition in dataFetchResult.WorkConditions)
                            ProcessItem(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, workCondition);
                        break;
                    case TreeType.RetryPolicy:
                        foreach (var workRetryPolicy in dataFetchResult.WorkRetryPolicies)
                            ProcessItem(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, workRetryPolicy);
                        break;
                    default:
                        // Workers
                        foreach (var worker in dataFetchResult.Workers)
                            ProcessWorker(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, worker);

                        // Work Items
                        foreach (var workItem in dataFetchResult.WorkItems)
                            ProcessItem(ids, dataFetchResult.ServerConnection, dataFetchResult.ServerManager, workItem);
                        break;
                }

                jMax = Count;
                for (int j = jMax - 1; j >= 0; j--)
                {
                    treeItem = this[j];
                    if (treeItem.ServerConnectionId != dataFetchResult.ServerConnection.ServerConnectionId)
                        continue;
                    if (!ids.Contains(treeItem.TreeItemId))
                    {
                        using (var manager = new Sidvall.Business.BusinessCollectionSettingsManager(this, Sidvall.Business.BusinessCollectionEvents.NotifyCollectionChanged))
                        {
                            Remove(treeItem);
                        }
                    }
                }
            }
        }
        private async Task ReloadAsync(System.Collections.ObjectModel.Collection<DataFetchResult> dataFetchResults, Sidvall.WorkItemManager.Data.TreeType type, int index)
        {
            var dataFetchResult = dataFetchResults[index];
            try
            {
                switch (type)
                {
                    case TreeType.WorkItemType:
                        var workItemTypeCriteria = ParameterManager.GetWorkItemTypeFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                        dataFetchResult.WorkItemTypes = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkItemTypeManager.GetItemsAsync(workItemTypeCriteria);
                        break;
                    case TreeType.Condition:
                        var workConditionCriteria = ParameterManager.GetWorkConditionFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                        dataFetchResult.WorkConditions = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkConditionManager.GetItemsAsync(workConditionCriteria);
                        break;
                    case TreeType.RetryPolicy:
                        var workRetryPolicyCriteria = ParameterManager.GetWorkRetryPolicyListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                        dataFetchResult.WorkRetryPolicies = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkRetryPolicyManager.GetItemsAsync(workRetryPolicyCriteria);
                        break;
                    default:
                        var workerCriteria = ParameterManager.GetWorkerFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                        dataFetchResult.Workers = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkerManager.GetItemsAsync(workerCriteria);
                        var workItemCriteria = ParameterManager.GetWorkItemFetchListCriteria(dataFetchResult.ServerConnection.ServerConnectionId.Value);
                        workItemCriteria.IncludedEntities = new Sidvall.Data.RelationCollection<Data.Relations.WorkItemRelation>();
                        workItemCriteria.IncludedEntities.Add(Data.Relations.WorkItemRelation.Worker);
                        dataFetchResult.WorkItems = await Sidvall.WorkItemManager.UI.SystemContext.Current.DataPortal.WorkItemManager.GetItemsAsync(workItemCriteria);
                        foreach (var workItem in dataFetchResult.WorkItems)
                        {
                            if (workItem.WorkItemTypeId != null)
                            {
                                var workItemProvider = dataFetchResult.ServerManager.GetWorkItemProvider(workItem);
                                if (workItemProvider != null)
                                    workItem.WorkItemRating = await workItemProvider.GetWorkItemRatingAsync();
                                else
                                    workItem.WorkItemRating = null;
                            }
                            else
                            {
                                workItem.WorkItemRating = null;
                            }
                        }
                        break;
                }
                dataFetchResult.Exception = null;
            }
            catch (System.Exception ex)
            {
                dataFetchResult.Exception = ex;
            }
        }

        #endregion

        #endregion
        #region Factory Members

        #region GetTreeItemCollection

        public static TreeItemCollection GetTreeItemCollection()
        {
            TreeItemCollection item;

            item = new TreeItemCollection();

            return item;
        }

        #endregion

        #endregion
        #region Private Members

        private const string FullNameSeparator = "/";

        #region ProcessServer

        private void ProcessServer(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    IsServer = true,
                    IsWorker = false,
                    IsItem = false,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value);
            SortItem(treeItem);
            treeItem.Name = serverConnection.Name;
            treeItem.FullName = serverConnection.Name;
            treeItem.IsEnabled = serverConnection.IsEnabled;
        }

        #endregion
        #region ProcessWorker

        private void ProcessWorker(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, IWorker worker)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, worker.WorkerId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    WorkerId = worker.WorkerId,
                    IsServer = false,
                    IsWorker = true,
                    IsItem = false,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, worker.WorkerId);
            SortItem(treeItem);
            treeItem.Name = worker.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + treeItem.Name;
            treeItem.IsEnabled = worker.IsEnabled;
        }

        #endregion
        #region ProcessItem

        private void ProcessItem(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, WorkItem workItem)
        {
            TreeItem treeItem, workerTreeItem;
            string key, workerKey;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, workItem.WorkerId, workItem.WorkItemId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    WorkerId = workItem.WorkerId,
                    ItemId = workItem.WorkItemId,
                    IsServer = false,
                    IsWorker = false,
                    IsItem = true,
                    ServerManager = serverManager,
                };
                Add(treeItem);
                workerKey = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, workItem.WorkerId);
                workerTreeItem = (from o in this
                                  where o.TreeItemId == workerKey
                                  select o).FirstOrDefault();
                workerTreeItem.ChildCount += 1;
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, workItem.WorkerId, workItem.ExecutionOrder);
            SortItem(treeItem);
            treeItem.Name = workItem.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + workItem.Worker.Name + FullNameSeparator + treeItem.Name;
            treeItem.IsEnabled = workItem.IsEnabled;
            treeItem.IsCanceled = workItem.IsCanceled;
            treeItem.IsForceExecution = workItem.ForceExecution;
            switch (workItem.WorkItemStatusId)
            {
                case Sidvall.WorkItemManager.Workflow.WorkItemStatus.Executing:
                    treeItem.IsExecuting = true;
                    break;
                default:
                    treeItem.IsExecuting = false;
                    break;
            }
            if (workItem.LastWorkItemResultId == null)
            {
                treeItem.LastResultIsCanceled = false;
                treeItem.LastResultIsFailed = false;
                treeItem.LastResultIsOk = false;
            }
            else
            {
                switch (workItem.LastWorkItemResultId.Value)
                {
                    case Sidvall.WorkItemManager.Workflow.WorkItemResultType.Ok:
                        treeItem.LastResultIsCanceled = false;
                        treeItem.LastResultIsFailed = false;
                        treeItem.LastResultIsOk = true;
                        break;
                    case Sidvall.WorkItemManager.Workflow.WorkItemResultType.Canceled:
                        treeItem.LastResultIsCanceled = true;
                        treeItem.LastResultIsFailed = false;
                        treeItem.LastResultIsOk = false;
                        break;
                    case Sidvall.WorkItemManager.Workflow.WorkItemResultType.Failed:
                        treeItem.LastResultIsCanceled = false;
                        treeItem.LastResultIsFailed = true;
                        treeItem.LastResultIsOk = false;
                        break;
                    default:
                        treeItem.LastResultIsCanceled = false;
                        treeItem.LastResultIsFailed = false;
                        treeItem.LastResultIsOk = false;
                        break;
                }
            }
            treeItem.ResultTooltip = GetResultTooltip(workItem);
            treeItem.StatusTooltip = GetStatusTooltip(workItem);
            treeItem.ItemTooltip = GetWorkItemTooltip(workItem);
            if (workItem.WorkItemRating != null)
            {
                switch (workItem.WorkItemRating.WorkItemRatingType)
                {
                    case WorkItemRatingType.Horrible:
                    case WorkItemRatingType.Poor:
                        treeItem.HasPoorRating = true;
                        break;
                    default:
                        treeItem.HasPoorRating = false;
                        break;
                }
            }
        }
        private void ProcessItem(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, WorkItemType item)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, null, item.WorkItemTypeId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    WorkerId = null,
                    ItemId = item.WorkItemTypeId,
                    IsServer = false,
                    IsWorker = false,
                    IsItem = true,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, item.Name);
            SortItem(treeItem);
            treeItem.Name = item.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + treeItem.Name;
            treeItem.IsEnabled = true;
        }
        private void ProcessItem(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, WorkCondition item)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, null, item.WorkConditionId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    WorkerId = null,
                    ItemId = item.WorkConditionId,
                    IsServer = false,
                    IsWorker = false,
                    IsItem = true,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, item.Name);
            SortItem(treeItem);
            treeItem.Name = item.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + treeItem.Name;
            treeItem.IsEnabled = true;
        }
        private void ProcessItem(System.Collections.Generic.HashSet<string> ids, IServerConnection serverConnection, ServerManager serverManager, WorkRetryPolicy item)
        {
            TreeItem treeItem;
            string key;

            key = TreeItem.GetTreeItemId(serverConnection.ServerConnectionId.Value, null, item.WorkRetryPolicyId);
            if (ids.Contains(key))
                return;
            ids.Add(key);
            treeItem = (from o in this
                        where o.TreeItemId == key
                        select o).FirstOrDefault();
            if (treeItem == null)
            {
                treeItem = new TreeItem()
                {
                    ServerConnectionId = serverConnection.ServerConnectionId.Value,
                    WorkerId = null,
                    ItemId = item.WorkRetryPolicyId,
                    IsServer = false,
                    IsWorker = false,
                    IsItem = true,
                    ServerManager = serverManager,
                };
                Add(treeItem);
            }
            else
            {
                treeItem.ServerManager = serverManager;
            }
            treeItem.SortOrder = TreeItem.GetSortOrder(serverConnection.ServerConnectionId.Value, item.Name);
            SortItem(treeItem);
            treeItem.Name = item.Name;
            treeItem.FullName = serverConnection.Name + FullNameSeparator + treeItem.Name;
            treeItem.IsEnabled = true;
        }

        #endregion

        #region GetWorkItemTooltip

        private string GetWorkItemTooltip(WorkItem item)
        {
            System.Text.StringBuilder toolTip;

            toolTip = new System.Text.StringBuilder();
            if (!item.IsEnabled)
                toolTip.Append("Task is disabled");
            else if (item.IsCanceled)
                toolTip.Append("Task is canceled");
            if (item.WorkItemRating != null)
            {
                if (!string.IsNullOrWhiteSpace(item.WorkItemRating.Description))
                    toolTip.Append(System.Environment.NewLine + item.WorkItemRating.Description);
            }
            if (toolTip.Length == 0)
                return "Task";
            return toolTip.ToString();
        }

        #endregion
        #region GetResultTooltip

        private string GetResultTooltip(WorkItem item)
        {
            if (item.LastWorkItemResultId != null)
            {
                if (item.LastExecutionStartTime != null)
                    return "Last Result: " + item.LastWorkItemResultId.ToString() + " (between " + item.LastExecutionStartTime.ToString() + " and " + item.LastExecutionEndTime.ToString() + ")";
                else
                    return "Last Result: " + item.LastWorkItemResultId.ToString();
            }
            return string.Empty;
        }

        #endregion
        #region GetStatusTooltip

        private string GetStatusTooltip(Sidvall.WorkItemManager.Data.IWorkItem item)
        {
            return "Status: " + item.WorkItemStatusId.ToString();
        }

        #endregion
        #region SortItem

        private void SortItem(TreeItem item)
        {
            TreeItem nextItem;
            int iMax, index;

            iMax = Count;
            index = IndexOf(item);
            while (index < iMax - 1)
            {
                nextItem = this[index + 1];
                if (item.SortOrder.CompareTo(nextItem.SortOrder) > 0)
                {
                    SwapItems(index, index + 1);
                    index += 1;
                }
                else
                {
                    break;
                }
            }
            while (index > 0)
            {
                nextItem = this[index - 1];
                if (item.SortOrder.CompareTo(nextItem.SortOrder) < 0)
                {
                    SwapItems(index, index - 1);
                    index -= 1;
                }
                else
                {
                    break;
                }
            }
        }

        #endregion
        #region SwapItems

        private void SwapItems(int index1, int index2)
        {
            TreeItem tmpItem;

            tmpItem = this[index1];
            this[index1] = this[index2];
            this[index2] = tmpItem;
        }

        #endregion

        #endregion
        #region Constructors

        private TreeItemCollection()
        {
        }

        #endregion
    }
}